// Package entity 定义工作流系统的核心实体
//
// 条件系统使用指南：
//
// 1. 节点类型说明
//
//   - 审批节点(Kind=1): 需要人工审批，支持OR/AND策略，审批完成后按NextNodeTemplateID流转
//
//   - 条件节点(Kind=2): 根据业务参数自动判断流转路径，无需人工干预
//
//     2. 条件节点配置步骤
//     a) 创建条件节点: NewNodeTemplate(id, name, "", NodeTemplateKindCondition, workflowID)
//     b) 配置条件表达式: 设置Conditions字段，定义多个expression
//     c) 每个expression包含: 参数名(Param) + 操作符(Operator) + 期望值(Value) + 目标节点(NextNodeTemplateID)
//
// 3. 条件评估流程
//   - 调用GetApprovalNodeTemplateID方法，传入ConditionParams参数
//   - 系统按Conditions数组顺序逐一评估条件
//   - 找到第一个满足的条件就返回对应的NextNodeTemplateID
//   - 建议配置默认分支避免所有条件都不满足的情况
//
// 4. 最佳实践
//
//   - 条件顺序很重要：将最具体的条件放在前面，通用条件放在后面
//
//   - 添加默认分支：最后一个条件使用"不等于null"作为兜底逻辑
//
//   - 参数类型一致：确保ConditionParams中的值类型与expression.Value类型匹配
//
//   - 避免条件冲突：确保条件之间的逻辑关系清晰，避免互相矛盾
//
//     5. 使用示例
//     // 创建条件节点
//     node := NewNodeTemplate("condition1", "金额判断", "", NodeTemplateKindCondition, "workflow1")
//
//     // 配置条件：金额>10000跳转到总监审批，否则跳转到经理审批
//     node.Conditions = []expression{
//     {Param: "amount", Operator: ">", Value: 10000, NextNodeTemplateID: "director"},  // 大于10000
//     {Param: "amount", Operator: "!=", Value: nil, NextNodeTemplateID: "manager"},    // 默认分支
//     }
//
//     // 运行时评估
//     params := ConditionParams{"amount": 15000}
//     nextNode := node.GetApprovalNodeTemplateID(params) // 返回"director"
package entity

import (
	"fmt"
	"strconv"
	"strings"
)

// 节点模板类型常量定义
// 工作流中的节点分为两种类型：审批节点和条件节点
const (
	// NodeTemplateKindApproval 审批类型节点
	// 审批节点需要人工审批，具有审批策略（OR/AND），审批完成后流转到下一个节点
	NodeTemplateKindApproval = 1
	// NodeTemplateKindCondition 条件类型节点
	// 条件节点根据业务参数自动判断流转路径，无需人工干预
	// 通过Conditions字段定义多个条件表达式，系统会按顺序评估条件，匹配第一个满足的条件进行跳转
	NodeTemplateKindCondition = 2

	// NodeTemplateKindStart 开始节点
	// 工作流入口，不承担审批责任，仅用于定义工作流的起点
	NodeTemplateKindStart = 3

	// NodeTemplateKindEnd 结束节点
	// 工作流出口，不承担审批责任，仅用于定义工作流的终点
	NodeTemplateKindEnd = 4
)

// ApprovalStrategy 审批策略类型
type ApprovalStrategy string

// 审批策略枚举
const (
	// ApprovalStrategyOr 或批策略：任意一人通过即可通过，任意一人拒绝即拒绝
	ApprovalStrategyOr ApprovalStrategy = "OR"
	// ApprovalStrategyAnd 与批策略：所有人通过才能通过，任意一人拒绝即拒绝
	ApprovalStrategyAnd ApprovalStrategy = "AND"
)

// NodeTemplate 节点模板实体
// 节点模板定义了工作流中节点的基本信息和行为规则
// 支持两种类型的节点：审批节点和条件节点
type NodeTemplate struct {
	// ID 节点模板唯一标识
	ID string `json:"id"`
	// Name 节点名称，用于显示和标识
	Name string `json:"name"`
	// NextNodeTemplateID 下一个节点的ID
	// 对于审批节点(Kind=1)：指向审批完成后的下一个节点
	// 对于条件节点(Kind=2)：此字段为空，跳转逻辑由Conditions字段控制
	NextNodeTemplateID string `json:"nextNodeTemplateID"`
	// Kind 节点类型
	// 1: 审批类型节点 - 需要人工审批决策
	// 2: 条件类型节点 - 根据条件自动跳转
	// 3: 开始节点 - 工作流入口，不承担审批责任，仅用于定义工作流的起点
	// 4: 结束节点 - 工作流出口，不承担审批责任，仅用于定义工作流的终点
	Kind int `json:"kind"`
	// WorkflowTemplateID 所属工作流模板的ID
	WorkflowTemplateID string `json:"workflowTemplateId"`
	// ApprovalStrategy 审批策略，仅对审批节点有效
	// 空值或OR表示或批策略：任意一人通过即可
	// AND表示与批策略：所有人都必须通过
	ApprovalStrategy ApprovalStrategy `json:"approvalStrategy"`
	// BusinessParams 业务参数，JSON格式存储业务相关的配置信息
	BusinessParams string `json:"businessParams"`

	ConditionRules []ConditionRule `json:"conditionRule"`
}

type ConditionRule struct {
	ConditionGroups []ConditionGroup `json:"conditionGroups"`
	// NextNodeTemplateID 条件组满足时跳转的目标节点ID
	// 当条件组的逻辑表达式评估为true时，工作流将跳转到此节点
	// 必须是有效的节点模板ID，否则会导致工作流执行异常
	NextNodeTemplateID string `json:"nextNodeTemplateId"`
}

// expression 条件表达式结构体
// LogicalOperator 逻辑操作符类型
type LogicalOperator string

const (
	// LogicalOperatorAnd AND逻辑操作符 - 所有条件都必须满足
	LogicalOperatorAnd LogicalOperator = "AND"
	// LogicalOperatorOr OR逻辑操作符 - 任意一个条件满足即可
	LogicalOperatorOr LogicalOperator = "OR"
)

// 用于定义条件节点中的单个条件判断逻辑
// 每个expression代表一个基础的"参数 操作符 值"的比较表达式
//
// 使用示例：
// 判断请假天数是否等于3天：
//
//	{
//	  "param": "leaveDays",     // 参数名：请假天数
//	  "operator": 1,             // 操作符：等于
//	  "value": 3                 // 比较值：3
//	}
type expression struct {
	// Param 参数名称
	// 对应ConditionParams中的键名，用于获取实际的参数值进行条件判断
	// 例如："amount"(金额)、"department"(部门)、"userLevel"(用户级别)等
	Param string `json:"param"`

	// Operator 比较操作符
	// 定义参数值与期望值的比较方式：
	// "==" - 等于，参数值等于指定值时条件成立
	// "!=" - 不等于，参数值不等于指定值时条件成立
	// ">" - 大于，参数值大于指定值时条件成立
	// "<" - 小于，参数值小于指定值时条件成立
	// ">=" - 大于等于，参数值大于等于指定值时条件成立
	// "<=" - 小于等于，参数值小于等于指定值时条件成立
	// "⊇" - 包含，参数值包含指定值时条件成立
	// "!⊇" - 不包含，参数值不包含指定值时条件成立
	Operator string `json:"operator"`

	// Value 期望值
	// 与参数值进行比较的目标值，支持任意类型(string、int、bool等)
	// 类型必须与ConditionParams中对应参数的类型兼容
	// 例如：字符串"manager"、数字100、布尔值true等
	Value any `json:"value"`

	// Kind 参数类型，用于指定如何解析和比较参数值
	// 支持的类型："string", "int", "float", "bool", "array", "object"
	Kind string `json:"kind"`
}

// NewExpression 创建expression实例的构造函数
// 用于在其他包中创建expression实例，因为expression类型是未导出的
// 参数:
//
//	param: 参数名称
//	operator: 比较操作符
//	value: 期望值
//
// 返回值:
//
//	expression:// NewExpression 创建一个新的表达式
func NewExpression(param string, operator string, value any, paramType string) expression {
	return expression{
		Param:    param,
		Operator: operator,
		Value:    value,
		Kind:     paramType,
	}
}

// ExpressionData 用于传递expression数据的结构体
// 因为expression是未导出类型，使用此结构体在包间传递数据
type ExpressionData struct {
	Param    string `json:"param"`
	Operator string `json:"operator"`
	Value    any    `json:"value"`
	Type     string `json:"type"`
}

// NewConditionGroup 创建ConditionGroup实例的构造函数
// 用于在其他包中创建包含expression的ConditionGroup
// 参数:
//
//	expressionDataList: expression数据列表
//	logicalOperator: 逻辑操作符
//
// 返回值:
//
//	ConditionGroup: 创建的ConditionGroup实例
func NewConditionGroup(expressionDataList []ExpressionData, logicalOperator LogicalOperator) ConditionGroup {
	conditions := make([]expression, len(expressionDataList))
	for i, data := range expressionDataList {
		conditions[i] = expression{
			Param:    data.Param,
			Operator: data.Operator,
			Value:    data.Value,
			Kind:     data.Type,
		}
	}

	return ConditionGroup{
		Conditions:      conditions,
		LogicalOperator: logicalOperator,
	}
}

// NewConditionRule 创建ConditionRule实例的构造函数
// 用于在其他包中创建包含ConditionGroups的ConditionRule
// 参数:
//
//	conditionGroups: 条件组列表
//	nextNodeTemplateID: 条件规则满足时跳转的目标节点ID
//
// 返回值:
//
//	ConditionRule: 创建的ConditionRule实例
func NewConditionRule(conditionGroups []ConditionGroup, nextNodeTemplateID string) ConditionRule {
	return ConditionRule{
		ConditionGroups:    conditionGroups,
		NextNodeTemplateID: nextNodeTemplateID,
	}
}

// ConditionGroup 条件组
// 用于定义一组条件的组合逻辑和跳转规则
// 支持多个条件通过AND/OR逻辑操作符进行组合
//
// 使用示例：
// 请假审批流程中，当"请假天数<=3 AND 部门=技术部"时跳转到直属领导审批：
//
//		{
//		  "conditions": [
//	    {"param": "leaveDays", "operator": "<=", "value": 3},
//	    {"param": "department", "operator": "==", "value": "技术部"}
//		  ],
//		  "logicalOperator": "AND",
//		  "nextNodeTemplateId": "tech_leader_approval"
//		}
type ConditionGroup struct {
	// Conditions 条件列表
	// 包含多个基础条件表达式，通过逻辑操作符进行组合
	Conditions []expression `json:"conditions"`

	// LogicalOperator 逻辑操作符
	// 定义条件之间的逻辑关系：
	// - "AND": 所有条件都必须满足
	// - "OR": 任意一个条件满足即可
	// 默认为"AND"
	LogicalOperator LogicalOperator `json:"logicalOperator"`
}

func (n NodeTemplate) IsLastNodeOfWorkflow() bool {
	return n.NextNodeTemplateID == ""
}

// GetApprovalStrategy 获取审批策略，默认为或批
func (n NodeTemplate) GetApprovalStrategy() ApprovalStrategy {
	if n.ApprovalStrategy == "" {
		return ApprovalStrategyOr
	}
	return n.ApprovalStrategy
}

// IsOrApproval 是否为或批策略
func (n NodeTemplate) IsOrApproval() bool {
	return n.GetApprovalStrategy() == ApprovalStrategyOr
}

// IsAndApproval 是否为与批策略
func (n NodeTemplate) IsAndApproval() bool {
	return n.GetApprovalStrategy() == ApprovalStrategyAnd
}

// NewNodeTemplate 创建节点模板实体的工厂方法
// 用于创建基础的节点模板，条件节点需要后续通过设置Conditions字段来配置条件逻辑
//
// 参数：
//
//	nodeTemplateID: 节点模板的唯一标识
//	name: 节点显示名称
//	nextNodeTemplateID: 下一个节点ID(仅对审批节点有效，条件节点应传空字符串)
//	kind: 节点类型(1=审批节点, 2=条件节点)
//	workflowTemplateID: 所属工作流模板ID
//
// 返回值：
//
//	NodeTemplate: 初始化的节点模板实例
//
// 注意事项：
// - 审批策略默认为OR(或批)
// - 条件数组初始化为空，条件节点需要后续手动设置Conditions字段
// - 条件节点的nextNodeTemplateID应为空，跳转逻辑由Conditions控制
func NewNodeTemplate(nodeTemplateID, name, nextNodeTemplateID string, kind int, workflowTemplateID string) NodeTemplate {
	return NodeTemplate{
		ID:                 nodeTemplateID,
		Name:               name,
		NextNodeTemplateID: nextNodeTemplateID,
		Kind:               kind,
		WorkflowTemplateID: workflowTemplateID,
		ApprovalStrategy:   ApprovalStrategyOr, // 默认为或批策略
		ConditionRules:     nil,                // 初始化为空，条件节点需要后续配置
	}
}

// GetNextApprovalNodeTemplateID 获取下一个审批节点的模板ID
// 这是条件评估的核心方法，根据节点类型和条件参数决定工作流的下一步流转
//
// 功能说明：
// 1. 对于审批节点(Kind=1)：直接返回当前节点ID，表示需要在当前节点进行审批
// 2. 对于条件节点(Kind=2)：根据条件参数评估跳转到哪个节点
//
// 条件评估算法：
// - 按照Conditions数组的顺序逐一评估每个条件表达式
// - 使用"短路"策略：找到第一个满足的条件就立即返回，不再评估后续条件
// - 如果所有条件都不满足，返回空字符串，表示配置错误或缺少默认分支
//
// 参数：
//
//	conditionParams: 条件参数映射表，包含用于条件判断的业务参数
//	                键为参数名(对应expression.Param)，值为实际的业务数据
//
// 返回值：
//
//	string: 下一个节点的模板ID
//	       - 审批节点：返回当前节点ID
//	       - 条件节点：返回满足条件的目标节点ID
//	       - 空字符串：表示条件节点没有匹配的条件(配置错误)
//
// 使用示例：
//
//	conditionParams := ConditionParams{
//	  "amount": 5000,
//	  "department": "IT",
//	}
//	nextNodeID := nodeTemplate.GetNextApprovalNodeTemplateID(conditionParams)
//
// EvaluateNodeConditions 评估当前节点的条件配置
// 根据当前节点的条件配置和传入的条件参数，确定条件判断的结果
// 注意：此方法仅评估当前单个节点的条件，不进行跨节点的路径查找
//
// 参数:
//
//	conditionParams: 条件参数，包含用于条件判断的各种参数值
//
// 返回值:
//
//	string: 条件评估结果对应的目标节点ID，如果没有匹配的条件则返回空字符串
func (n NodeTemplate) EvaluateNodeConditions(conditionParams ConditionParams) string {
	// 实现步骤：
	// 1. 判断节点类型，审批节点直接返回当前节点ID
	// 2. 条件节点按顺序评估所有条件组
	// 3. 找到第一个满足的条件组就返回对应的目标节点ID
	// 4. 所有条件组都不满足则返回空字符串

	// 审批类型节点：需要在当前节点进行审批，返回当前节点ID
	if n.IsApprovalNode() {
		return n.ID
	}

	// 使用ConditionRules结构
	if len(n.ConditionRules) > 0 {
		return n.evaluateConditionRules(conditionParams)
	}

	// 没有配置任何条件，返回空字符串表示配置错误
	return ""
}

// GetNextApprovalNodeTemplateID 获取下一个审批节点的模板ID
// Deprecated: 请使用 FindNextApprovalNode 方法替代，该方法仅保留用于向后兼容
// 此方法将在未来版本中移除
func (n NodeTemplate) GetNextApprovalNodeTemplateID(conditionParams ConditionParams) string {
	return n.EvaluateNodeConditions(conditionParams)
}

// evaluateConditionRules 评估条件规则
// 遍历所有条件规则，按顺序评估每个ConditionRule是否满足
// 找到第一个满足的条件规则并返回其目标节点ID
//
// 评估逻辑：
// 1. 按照ConditionRules数组顺序逐一评估每个条件规则
// 2. 对于每个ConditionRule，评估其包含的所有ConditionGroup
// 3. ConditionRule内的ConditionGroup之间为OR关系：任意一个ConditionGroup满足即可
// 4. 如果ConditionRule满足，返回其NextNodeTemplateID
// 5. 如果所有ConditionRule都不满足，返回空字符串
func (n NodeTemplate) evaluateConditionRules(conditionParams ConditionParams) string {
	for _, rule := range n.ConditionRules {
		if n.evaluateConditionRule(rule, conditionParams) {
			return rule.NextNodeTemplateID
		}
	}
	return ""
}

// evaluateConditionRule 评估单个条件规则
// 条件规则内的条件组之间为OR关系（任意一个条件组满足即可）
func (n NodeTemplate) evaluateConditionRule(rule ConditionRule, conditionParams ConditionParams) bool {
	for _, group := range rule.ConditionGroups {
		if n.evaluateConditionGroup(group, conditionParams) {
			return true
		}
	}
	return false
}

// evaluateConditionGroup 评估单个条件组
// 根据逻辑操作符（AND/OR）计算条件组中所有条件的结果
func (n NodeTemplate) evaluateConditionGroup(group ConditionGroup, conditionParams ConditionParams) bool {
	if len(group.Conditions) == 0 {
		return false
	}

	// 获取逻辑操作符，默认为AND
	logicalOp := group.LogicalOperator
	if logicalOp == "" {
		logicalOp = LogicalOperatorAnd
	}

	// 根据逻辑操作符进行评估
	switch logicalOp {
	case LogicalOperatorAnd:
		// AND逻辑：所有条件都必须满足
		for _, condition := range group.Conditions {
			if !n.evaluateExpression(condition, conditionParams) {
				return false
			}
		}
		return true
	case LogicalOperatorOr:
		// OR逻辑：任意一个条件满足即可
		for _, condition := range group.Conditions {
			if n.evaluateExpression(condition, conditionParams) {
				return true
			}
		}
		return false
	default:
		// 未知逻辑操作符，默认使用AND逻辑
		for _, condition := range group.Conditions {
			if !n.evaluateExpression(condition, conditionParams) {
				return false
			}
		}
		return true
	}
}

// evaluateExpression 评估单个表达式
// 支持点号路径访问和类型解析
func (n NodeTemplate) evaluateExpression(expr expression, conditionParams ConditionParams) bool {
	// 使用点号路径和类型获取参数值
	var paramValue any
	var exists bool

	if expr.Kind != "" {
		// 如果指定了类型，使用类型化获取
		paramValue, exists = conditionParams.GetTypedValue(expr.Param, expr.Kind)
	} else {
		// 否则使用普通路径获取
		paramValue, exists = conditionParams.GetValueByPath(expr.Param)
	}

	// 如果参数不存在，返回false
	if !exists {
		return false
	}

	// 根据操作符进行比较
	switch expr.Operator {
	case "==":
		return n.compareValues(paramValue, expr.Value) == 0
	case "!=":
		return n.compareValues(paramValue, expr.Value) != 0
	case ">":
		return n.compareValues(paramValue, expr.Value) > 0
	case "<":
		return n.compareValues(paramValue, expr.Value) < 0
	case ">=":
		return n.compareValues(paramValue, expr.Value) >= 0
	case "<=":
		return n.compareValues(paramValue, expr.Value) <= 0
	case "⊇":
		return n.containsValue(paramValue, expr.Value)
	case "!⊇":
		return !n.containsValue(paramValue, expr.Value)
	default:
		return false
	}
}

// compareValues 比较两个值的大小
// 支持数值类型的比较，返回值：-1(小于)、0(等于)、1(大于)
func (n NodeTemplate) compareValues(value1, value2 any) int {
	// 尝试转换为float64进行数值比较
	if v1, ok1 := n.toFloat64(value1); ok1 {
		if v2, ok2 := n.toFloat64(value2); ok2 {
			if v1 < v2 {
				return -1
			} else if v1 > v2 {
				return 1
			}
			return 0
		}
	}

	// 非数值类型，转换为字符串进行字典序比较
	s1 := fmt.Sprintf("%v", value1)
	s2 := fmt.Sprintf("%v", value2)
	if s1 < s2 {
		return -1
	} else if s1 > s2 {
		return 1
	}
	return 0
}

// containsValue 检查value1是否包含value2
// 支持字符串包含检查
func (n NodeTemplate) containsValue(value1, value2 any) bool {
	s1 := fmt.Sprintf("%v", value1)
	s2 := fmt.Sprintf("%v", value2)
	return strings.Contains(s1, s2)
}

// toFloat64 尝试将任意类型转换为float64
func (n NodeTemplate) toFloat64(value any) (float64, bool) {
	switch v := value.(type) {
	case int:
		return float64(v), true
	case int8:
		return float64(v), true
	case int16:
		return float64(v), true
	case int32:
		return float64(v), true
	case int64:
		return float64(v), true
	case uint:
		return float64(v), true
	case uint8:
		return float64(v), true
	case uint16:
		return float64(v), true
	case uint32:
		return float64(v), true
	case uint64:
		return float64(v), true
	case float32:
		return float64(v), true
	case float64:
		return v, true
	case string:
		if f, err := strconv.ParseFloat(v, 64); err == nil {
			return f, true
		}
	}
	return 0, false
}

func (n NodeTemplate) IsApprovalNode() bool {
	return n.Kind == NodeTemplateKindApproval
}
